"""Project business logic service."""

from __future__ import annotations

from sqlalchemy import delete, func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.project import Project
from app.schemas.project import ProjectCreate, ProjectUpdate
from app.services.base import BaseService


class ProjectService(BaseService[Project]):
    """Project service with CRUD operations and custom business logic.

    Inherits standard CRUD operations from BaseService:
    - get_by_id: Get project by ID
    - create: Create new project
    - update: Update existing project
    - delete: Delete project by ID
    - delete_many: Batch delete by IDs
    - count: Count projects with filters

    Custom methods:
    - get_projects_with_search: Advanced filtering with search and status
    """

    def __init__(self, session: AsyncSession):
        """Initialize project service with database session.

        Args:
            session: SQLAlchemy async session
        """
        super().__init__(session, Project)

    async def get_projects_with_search(
        self,
        skip: int = 0,
        limit: int = 100,
        owner_id: int | None = None,
        search: str | None = None,
        status: str | None = None,
    ) -> tuple[list[Project], int]:
        """Get projects with advanced filtering including search.

        This method extends BaseService.get_many() with custom search logic
        that searches across multiple fields (name, description).

        Args:
            skip: Number of records to skip (offset)
            limit: Maximum number of records to return
            owner_id: Filter by project owner
            search: Search term for name/description (case-insensitive)
            status: Filter by project status

        Returns:
            Tuple of (list of projects, total count)
        """
        query = select(Project)

        # Apply filters
        if owner_id is not None:
            query = query.where(Project.owner_id == owner_id)

        if search:
            search_pattern = f"%{search}%"
            query = query.where(or_(Project.name.ilike(search_pattern), Project.description.ilike(search_pattern)))

        if status:
            query = query.where(Project.status == status)

        # Count total matching records
        count_stmt = select(func.count()).select_from(query.subquery())
        total_result = await self.session.execute(count_stmt)
        total = total_result.scalar_one()

        # Apply ordering and pagination
        query = query.order_by(Project.created_at.desc()).offset(skip).limit(limit)

        # Execute query
        result = await self.session.execute(query)
        projects = list(result.scalars().all())

        return projects, total

    async def create_project(self, project: ProjectCreate, owner_id: int) -> Project:
        """Create a new project with owner.

        This is a convenience wrapper around BaseService.create() that
        handles ProjectCreate schema and owner_id injection.

        Args:
            project: Project creation schema
            owner_id: ID of the project owner

        Returns:
            Created project instance
        """
        return await self.create(**project.model_dump(), owner_id=owner_id)

    async def update_project(self, project_id: int, project: ProjectUpdate) -> Project | None:
        """Update an existing project.

        This is a convenience wrapper around BaseService.update() that
        handles ProjectUpdate schema with exclude_unset.

        Args:
            project_id: Project ID to update
            project: Project update schema

        Returns:
            Updated project instance or None if not found
        """
        update_data = project.model_dump(exclude_unset=True)
        return await self.update(project_id, **update_data)

    async def batch_delete_by_owner(self, project_ids: list[int], owner_id: int) -> int:
        """Batch delete projects owned by specific user.

        This method ensures that only projects owned by the specified user
        are deleted, adding an extra security layer beyond delete_many().

        Args:
            project_ids: List of project IDs to delete
            owner_id: Owner ID for authorization check

        Returns:
            Number of projects deleted
        """
        if not project_ids:
            return 0

        stmt = delete(Project).where(Project.id.in_(project_ids), Project.owner_id == owner_id)
        result = await self.session.execute(stmt)
        await self.session.flush()
        return result.rowcount or 0
